﻿using System;
using System.Linq;

namespace YK.Infrastructure.NH
{
    using ISessionFactory = global::NHibernate.ISessionFactory;
    using NHibernate.Tool.hbm2ddl;
    using NHibernate.Cfg;
    using NHibernate.Mapping.ByCode;
    using YK.Component.Tools;
    using System.Reflection;

    public class DatabaseFactory : Disposable, IDatabaseFactory
    {
        private static readonly object sessionFactorySyncLock = new object();

        private string providerName;

        private string connectionString;

        private Database database;

        private static ISessionFactory sessionFactory;

        private  Configuration configuration;

        public DatabaseFactory(string providerName, string connectionString)
        {
            this.providerName = providerName;
            this.connectionString = connectionString;
        }

        public Database Get()
        {
            if (database == null)
            {
                InitSessionFactory();
                database = new Database(sessionFactory.OpenSession());
            }
            return database;
        }

        private void InitSessionFactory()
        {
            if (sessionFactory == null)
            {
                lock (sessionFactorySyncLock)
                {
                    if (sessionFactory == null)
                    {
                        Configuration();
                        AddMapping();
                        sessionFactory = configuration.BuildSessionFactory();
                    }
                }
            }
        }

        private void Configuration()
        {
            //:TODO 日记记录
            //logger = Logger.GetLogger(typeof(NHibernateManager));
            string dialect;
            string driver;
            configuration = new Configuration();

            //if (System.Configuration.ConfigurationManager.ConnectionStrings.Count > 0)
            //{
            //    System.Configuration.ConnectionStringSettings settings =
            //        System.Configuration.ConfigurationManager.ConnectionStrings[0];

            //    connectionString = settings.ConnectionString;
            //    providerName = settings.ProviderName;
            //}


            GetDialectAndDriver(this.providerName, out dialect, out driver);

            configuration.SetProperty("connection.provider", "NHibernate.Connection.DriverConnectionProvider");
            configuration.SetProperty("dialect", dialect);
            configuration.SetProperty("connection.driver_class", driver);
            configuration.SetProperty("connection.connection_string", this.connectionString);
            configuration.SetProperty("show_sql", "true");
        }

        private void AddMapping()
        {
            var mapper = new ModelMapper();

            var tmp = Assembly.Load("YK.Infrastructure.NH").GetTypes().
                      Where(m => m.Namespace == "YK.Infrastructure.NH.Mapping").ToList();
            mapper.AddMappings(tmp);
            //mapper.AddMapping(typeof(UserMapping));
            //mapper.AddMapping(typeof(RoleMapping));
            //mapper.AddMappings(typeof(EntityBase).Assembly.GetExportedTypes());
           
            var hbmMapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
            Console.WriteLine(hbmMapping.AsString());

            configuration.AddDeserializedMapping(hbmMapping, "");

        }

        private static void GetDialectAndDriver(string providerName, out string dialect, out string driver)
        {
            var objects = new[]
           {
               new {Name="DB2", Dialect="NHibernate.Dialect.DB2Dialect", Driver="NHibernate.Driver.DB2Driver"},
               new {Name="DB2400", Dialect="NHibernate.Dialect.DB2400Dialect", Driver="NHibernate.Driver.DB2400Driver"},

               new {Name="Firebird", Dialect="NHibernate.Dialect.FirebirdDialect", Driver="NHibernate.Driver.FirebirdDriver"},
               
               new {Name="MsSql2000", Dialect="NHibernate.Dialect.MsSql2000Dialect", Driver="NHibernate.Driver.SqlClientDriver"},
               new {Name="MsSql2005", Dialect="NHibernate.Dialect.MsSql2005Dialect", Driver="NHibernate.Driver.SqlClientDriver"},
               new {Name="MsSql2008", Dialect="NHibernate.Dialect.MsSql2008Dialect", Driver="NHibernate.Driver.Sql2008Driver"},
               new {Name="MsSqlCe", Dialect="NHibernate.Dialect.MsSqlCeDialect", Driver="NHibernate.Driver.SqlServerCeDriver"},

               new {Name="MySQL", Dialect="NHibernate.Dialect.MySQLDialect", Driver="NHibernate.Driver.MySqlDataDriver"},
               new {Name="MySQL5", Dialect="NHibernate.Dialect.MySQLDialect", Driver="NHibernate.Driver.MySqlDataDriver"},

                 // 1.NHibernate.Driver.OracleDataClientDriver (odp.net)   2. NHibernate.Driver.OracleClientDriver (ado.net)
               new {Name="Oracle8i", Dialect="NHibernate.Dialect.Oracle8iDialect", Driver="NHibernate.Driver.OracleDataClientDriver"},
               new {Name="Oracle9i", Dialect="NHibernate.Dialect.Oracle9iDialect", Driver="NHibernate.Driver.OracleDataClientDriver"},                                                               
               new {Name="Oracle10g", Dialect="NHibernate.Dialect.Oracle10gDialect", Driver="NHibernate.Driver.OracleDataClientDriver"},

               new {Name="PostgreSQL", Dialect="NHibernate.Dialect.PostgreSQLDialect", Driver="NHibernate.Driver.NpgsqlDriver"},
               new {Name="PostgreSQL81", Dialect="NHibernate.Dialect.PostgreSQL81Dialect", Driver="NHibernate.Driver.NpgsqlDriver"},
               new {Name="PostgreSQL82", Dialect="NHibernate.Dialect.PostgreSQL82Dialect", Driver="NHibernate.Driver.NpgsqlDriver"},
          
               new {Name="SQLite", Dialect="NHibernate.Dialect.SQLiteDialect", Driver="NHibernate.Driver.SQLite20Driver"}
           };

            var o = objects.Single(x => x.Name == providerName);

            if (o != null)
            {
                dialect = o.Dialect;
                driver = o.Driver;

                return;
            }

            dialect = String.Empty;
            driver = String.Empty;
            return;
        }

        private void BuildSchema(Configuration config)
        {
            // this NHibernate tool takes a configuration (with mapping info in)
            // and exports a database schema from it
            new SchemaExport(config).Execute(false, true, false);
        }

    }
}
